import store from '@/store'
import { xhrJsonRequest, xhrUploadFile, xhrDownloadFile, httpGetParamHandle } from '@/utils/request'
import { threadSleep } from '@/utils/common'

// 后台服务域名
const baseAdminDomain = process.env.VUE_APP_BASE_ADMIN_API
// 文件服务域名
const fileServerDomain = process.env.VUE_APP_UPLOAD_FILE_API
// 刷新token接口地址
const refreshTokenUrl = process.env.VUE_APP_BASE_ADMIN_API + '/AppAdmin/SysLogin/RefreshToken'

// 调试模式
const debug = false

/**
 * 验证指定接口是否有权限 验证按钮是否显示
 * @param {*} apiAuth
 */
export const verifyApiAuth = async(apiAuth) => {
  // console.log(apiAuth)
  for (const k in apiAuth) {
    const item = apiAuth[k]

    // 直接通过
    // item.result = true

    // 验证接口是否有权限
    const vr = await item.api(null, null, null, 4)
    if (vr) {
      item.result = true
    }
  }
}

/**
 * Api请求处理 api调用、权限验证、获取接口信息 可刷新token登录状态 自动提示错误消息
 * @param {*} path 请求路径不包含域名
 * @param {*} data 请求参数如{id:1, name:'zhangsan'} 最常用的数据传输对象 get类型则处理成?id=1&name=zhangsan post类型则转换成json字符串上传同时可用urlParam
 * @param {*} method 请求方式GET POST…… 注意GET类型使用data传参数 非GET类型全部使用urlParam做url参数不可用data传url参数
 * @param {*} vue vue实例this
 * @param {*} loadingKey 加载key
 * @param {*} operation 操作类型
 * 0执行json类型请求调用
 * 1下载文件建议用get:data不可用urlParam
 * 2上传文件建议使用post:formData、urlParam不可用data传url参数
 * 3上传到文件服务
 * 4授权检查
 * 5获取api路径
 * 6获取api包含域名的路径
 * @param {*} urlParam 非get请求传url参数如{id:1, name:'zhangsan'}处理成?id=1&name=zhangsan 一般用于提交数据或上传文件时body不方便传参数时使用此字段
 * @param {*} formData formData实例 new FormData() 然后使用append添加文件信息
 * @param {*} proc 上传进度处理方法回调 proc(loaded, total, e){}
 */
export async function apiRequest(path, data = {}, method = 'GET', vue = null, loadingKey = null, operation = 0, urlParam = {}, formData = null, proc = null) {
  // 初始化数据
  var header = {
    // 'uId': store.state.user.uId,
    'Authorization': store.state.user.token
  }
  if (path.indexOf('http://') === 0 || path.indexOf('https://') === 0) {
    throw new Error('path不能包含域名:' + path)
  }
  if (!data) {
    data = {}
  }
  if (!urlParam) {
    urlParam = {}
  }
  var url = baseAdminDomain + path
  if (method !== 'GET') {
    url = url + httpGetParamHandle(urlParam)
  }

  // 递归验证接口权限方法
  const verifyApiAuth = modules => {
    for (let i = 0; i < modules.length; i++) {
      const m = modules[i]
      if (m && m.auth) {
        for (let j = 0; j < m.auth.length; j++) {
          if (m.auth[j] === path) {
            // if (debug) {
            //   console.log('验证权限成功：' + url)
            // }
            return true
          }
        }
      }
      if (m.children) {
        if (verifyApiAuth(m.children)) {
          return true
        }
      }
    }
    return false
  }
  // 执行请求方法 返回408会刷新token续期
  const fetchApi = async(xhrFunc) => {
    // 准备发送请求
    if (vue && loadingKey) {
      vue[loadingKey] = true
    }
    if (debug) {
      console.log('开始请求：' + url)
    }
    // await threadSleep(400)
    var resp = await xhrFunc()
    if (resp.code === 0) {
      if (vue && loadingKey) {
        // eslint-disable-next-line require-atomic-updates
        vue[loadingKey] = false
      }
      if (debug) {
        console.log('结束请求：' + url)
      }
      return resp
    } else if (resp.code === 408) {
      // 准备刷新token数据
      const oldRefToken = store.state.user.refToken
      const reqData = {
        // 'uId': store.state.user.uId,
        'Authorization': store.state.user.token,
        'refToken': ''
      }
      // token失效刷新token 如果同时有多个请求进来则等待之前的刷新完成
      if (store.state.user.token && store.state.user.refToken && store.state.user.uId) {
        // 清除刷新token防止其他请求重复调用刷新token store.state.user.refToken = ''
        store.commit('user/SET_TOKEN', reqData)
        reqData.refToken = oldRefToken
        if (debug) {
          console.log('开始刷新token重置：' + reqData.refToken)
        }
        const refResp = await xhrJsonRequest(refreshTokenUrl, reqData, 'POST', {})
        if (refResp.ok) {
          if (debug) {
            console.log('刷新token成功！')
          }
          // 刷新token成功则保存本地新的token重新调接口 再出现错误则需要重新登录
          store.commit('user/SET_TOKEN', refResp.body)
          resp = await xhrFunc()
          if (resp.code !== 0 && vue) {
            if (vue) {
              // 重新调接口失败 提示错误结束
              vue.$message({ type: 'error', message: resp.message })
            }
          }
          if (vue && loadingKey) {
            // eslint-disable-next-line require-atomic-updates
            vue[loadingKey] = false
          }
          return resp
        } else {
          if (vue) {
            if (loadingKey) {
              // eslint-disable-next-line require-atomic-updates
              vue[loadingKey] = false
            }
            // 刷新token失败提示错误直接调到登录页
            vue.$message({ type: 'error', message: '刷新登录状态失败，您的账号可能在其他地方登录！' })
            vue.$router.push('/login')
          }
          return refResp
        }
      } else {
        // 之前可能有接口已经调用刷新token 在此等待token刷新
        if (store.state.user.token && store.state.user.uId) {
          for (let i = 0; i < 10; i++) {
            if (debug) {
              console.log('等待token刷新中……')
            }
            await threadSleep(500)
            if (store.state.user.refToken) {
              resp = await xhrFunc(url, data, method, { 'uId': store.state.user.uId, 'token': store.state.user.token })
              if (resp.code !== 0 && vue) {
                if (vue) {
                  // 重新调接口失败 提示错误结束
                  vue.$message({ type: 'error', message: resp.message })
                }
              }
              if (vue && loadingKey) {
                // eslint-disable-next-line require-atomic-updates
                vue[loadingKey] = false
              }
              return resp
            }
          }
        }
        // 保存的登录信息不足以调用刷新授权接口提示错误直接跳到登录页
        if (vue) {
          if (loadingKey) {
            // eslint-disable-next-line require-atomic-updates
            vue[loadingKey] = false
          }
          vue.$message({ type: 'error', message: '登陆状态失效，您的账号可能在其他地方登录！' })
          vue.$router.push('/login')
        }
        // 没传vue实例无法跳转则直接返回token失效
        return resp
      }
    } else if (resp.code === 610) {
      // 许可证失效
      if (vue) {
        if (loadingKey) {
          // eslint-disable-next-line require-atomic-updates
          vue[loadingKey] = false
        }
        vue.$message({ type: 'error', message: resp.message })
        // 许可证异常 暂时提示错误
        // vue.$router.push({
        //   path: '/610',
        //   query: {
        //     license: resp.body.deviceCode
        //   }
        // })
      }
    } else {
      // 不是成功响应自动弹出提示除了 612
      if (resp.code !== 612 && vue) {
        if (loadingKey) {
          // eslint-disable-next-line require-atomic-updates
          vue[loadingKey] = false
        }
        vue.$message({ type: 'error', message: resp.message })
      }
      // 未登录则跳转到登录页
      if (resp.code === 401) {
        vue.$router.push('/login')
      }
    }

    return resp
  }
  // 执行对应操作
  switch (operation) {
    case 0:
      return await fetchApi(() => xhrJsonRequest(url, data, method, header))
      // return fetchApi(() => xhrJsonRequest(url, data, method, header))
    case 1:
      // 下载文件
      return await fetchApi(() => xhrDownloadFile(url, data, method, header))
    case 2:
      // 上传文件到本站点
      return await fetchApi(() => xhrUploadFile(url, formData, urlParam, method, header))
    case 3:
      // 上传文件到文件服务
      return await fetchApi(() => xhrUploadFile(fileServerDomain + path, formData, urlParam, method, header, proc))
    case 4:
      return verifyApiAuth(store.state.user.authInfo)
    case 5:
      return path
    case 6:
      return url

    default:
      throw new Error('请输入正确的operation参数')
  }
}

/**
 * 使用url下载文件方法 不支持传请求头 暂时不使用
 * @param {*} url
 * @param {*} data 是get参数如{id:1, name:''}
 * @param {*} vue vue实例this
 * @param {*} loadingKey 加载key
 */
export function downloadFile(url, data, vue, loadingKey) {
  let dataStr = ''
  if (data) {
    Object.keys(data).forEach(key => {
      dataStr += key + '=' + data[key] + '&'
    })
    if (dataStr !== '') {
      dataStr = dataStr.substr(0, dataStr.lastIndexOf('&'))
      url = url + '?' + dataStr
    }
  }
  if (!vue) {
    alert('请传入vue实例this！')
  }
  if (vue && loadingKey) {
    vue[loadingKey] = true
  }
  window.location.href = url +
    '&token=' + vue.$store.state.user.token +
    '&refToken=' + vue.$store.state.user.refToken +
    '&uId=' + vue.$store.state.user.uId +
    '&domain=' + window.location.origin
  if (vue && loadingKey) {
    vue[loadingKey] = false
  }
}

// 登陆 以下方法都是异步方法调用时前面带上await
export const sysUserLogin = (data, vue, loadingKey, operation, urlParam) => apiRequest('/AppAdmin/SysLogin/Token', data, 'POST', vue, loadingKey, operation, urlParam)

// 退出
export const sysUserSignOut = (vue, loadingKey, operation, urlParam) => apiRequest('/AppAdmin/SysLogin/SysExit', null, 'GET', vue, loadingKey, operation, urlParam)

// 获取子系统权限
export const sysUserGetAuth = (data, vue, loadingKey, operation, urlParam) => apiRequest('/AppAdmin/SysUser/GetAuth', data, 'GET', vue, loadingKey, operation, urlParam)

// 枚举键值对
export const kvEnum = (data, vue, loadingKey, operation, urlParam) => apiRequest('/AppAdmin/KeyValue/ComEnum', data, 'GET', vue, loadingKey, operation, urlParam)

// 获取用户信息 暂不用
export const getAdminInfo = (vue, loadingKey, operation, urlParam) => apiRequest('/AppAdmin/SysLogin/GetUserInfo', null, 'GET', vue, loadingKey, operation, urlParam)
